HEAD ======= >>>>>>> origin/main
library(tidyverse)
library(ggplot2)
In this case study, we seek to determine the best biomarkers of recent THC use within a 3-hour period by evaluating compounds across three matrices: blood, oral fluid, and breath. Additionally, we aim to determine the optimal THC concentration cutoffs for each matrix within this timeframe and to examine how treatment groups may influence THC levels. This analysis is important given the legal implications of THC detection in impaired driving cases. Nineteen states have implemented per se or zero-tolerance laws for THC, with per se limits varying between 1-5 ng/mL THC in blood 1. These limits suggest significant regulatory interest in identifying reliable biological markers of recent cannabis use, particularly in roadside testing contexts.
The study’s dataset includes three matrices–blood, oral fluid, and breath–with a total of 573 participants across them. Blood and oral fluid matrices allow detection of multiple THC-related compounds (8 in blood, 7 in oral fluid), while breath analysis focuses solely on one compound. Blood and oral fluid matrices have previously demonstrated relatively stable THC levels over time, providing a basis for exploring how they reflect THC concentrations within a 3-hour window 2. Breath testing, however, remains a newer field of investigation, and its reliability for THC detection within a short timeframe is still under review. Identifying the optimal biological matrix for THC detection in recent use is important for law enforcement to establish reliable, science-based standards for roadside impairment testing.
The analysis also seeks to address the legal challenges posed by varying state regulations. For example, states like Colorado utilize a “reasonable inference” approach, where blood concentrations above 5 ng/ml THC suggest impairment 3. These differing legal standards show the importance of identifying clear, evidence-based cutoff values for THC detection across different matrices. In this case study, we aim to determine which matrix is most effective for detecting recent THC use within a 3-hour window and to identify the optimal cutoff values for each matrix. This analysis will help us understand how different matrices perform in detecting THC levels over time and provide insights into their potential effectiveness in practical settings.
WB <- read_csv("data/Blood.csv")
BR <- read_csv("data/Breath.csv")
OF <- read_csv("data/OF.csv")
=======
WB <- read_csv("data/Blood.csv")
## Rows: 1525 Columns: 14
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (5): ID, Treatment, Group, FLUID TYPE, Timepoint
## dbl (9): CBN, CBD, THC, 11-OH-THC, THC-COOH, THC-COOH-Gluc, CBG, THC-V, time...
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
BR <- read_csv("data/Breath.csv")
## Rows: 949 Columns: 7
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (5): ID, Treatment, Group, Fluid, Timepoint
## dbl (2): THC (pg/pad), time.from.start
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
OF <- read_csv("data/OF.csv")
## Rows: 953 Columns: 13
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (5): ID, Treatment, Group, Fluid, Timepoint
## dbl (8): CBN, CBD, THC, 11-OH-THC, CBG, THC-V, THCA-A, time.from.start
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
>>>>>>> origin/main
WB <- WB |>
mutate(Treatment = fct_recode(Treatment,
"5.9% THC (low dose)" = "5.90%",
"13.4% THC (high dose)" = "13.40%"),
Treatment = fct_relevel(Treatment, "Placebo", "5.9% THC (low dose)")) |>
janitor::clean_names() |>
rename(thcoh = x11_oh_thc,
thccooh = thc_cooh,
thccooh_gluc = thc_cooh_gluc,
thcv = thc_v) |>
mutate(timepoint = case_when(time_from_start < 0 ~ "pre-smoking",
time_from_start > 0 & time_from_start <= 60 ~ "0-60 min",
time_from_start > 61 & time_from_start <= 120 ~ "0-120 min",
time_from_start > 121 & time_from_start <= 180 ~ "0-180 min",
time_from_start > 180 ~ "181+ min"))
OF <- OF |>
mutate(Treatment = fct_recode(Treatment,
"5.9% THC (low dose)" = "5.90%",
"13.4% THC (high dose)" = "13.40%"),
Treatment = fct_relevel(Treatment, "Placebo", "5.9% THC (low dose)")) |>
janitor::clean_names() |>
rename(thcoh = x11_oh_thc,
thcv = thc_v,
fluid_type = fluid) |>
mutate(timepoint = case_when(time_from_start < 0 ~ "pre-smoking",
time_from_start > 0 & time_from_start <= 60 ~ "0-60 min",
time_from_start > 61 & time_from_start <= 120 ~ "0-120 min",
time_from_start > 121 & time_from_start <= 180 ~ "0-180 min",
time_from_start > 180 ~ "181+ min"))
BR <- BR |>
mutate(Treatment = fct_recode(Treatment,
"5.9% THC (low dose)" = "5.90%",
"13.4% THC (high dose)" = "13.40%"),
Treatment = fct_relevel(Treatment, "Placebo", "5.9% THC (low dose)")) |>
janitor::clean_names() |>
rename(thc = thc_pg_pad,
fluid_type = fluid) |>
mutate(timepoint = case_when(time_from_start < 0 ~ "pre-smoking",
time_from_start > 0 & time_from_start <= 60 ~ "0-60 min",
time_from_start > 61 & time_from_start <= 120 ~ "0-120 min",
time_from_start > 121 & time_from_start <= 180 ~ "0-180 min",
time_from_start > 180 ~ "181+ min"))
combined <- bind_rows(list(WB, OF, BR))
write_csv(combined, "combined_data.csv")
combined_long <- combined |>
select(1:5,time_from_start,everything()) |>
pivot_longer(7:15)
invisible(ggplot(combined_long, aes(x = value)) +
geom_histogram() +
facet_wrap(~name))
invisible(combined_long |>
mutate(group_compound = paste0(fluid_type, ": ", name)) |>
ggplot(aes(x = value)) +
geom_histogram() +
facet_wrap(~group_compound, scales = "free"))
invisible(combined_long |>
filter(name == "thc") |>
ggplot(aes(x = group, y = value)) +
geom_boxplot() +
facet_wrap(~fluid_type, scales = "free"))
invisible(combined_long |>
filter(name == "thc") |>
ggplot(aes(x = treatment, y = value)) +
geom_boxplot() +
facet_wrap(~fluid_type, scales = "free"))
invisible(combined_long |>
filter(name == "thc", (timepoint == "0-30 min" | timepoint == "0-40 min")) |>
ggplot(aes(x = treatment, y = value)) +
geom_boxplot() +
facet_wrap(~fluid_type, scales = "free"))
plot_scatter_time <- function(matrix) {
combined_long |>
filter(!is.na(time_from_start), fluid_type==matrix) |>
ggplot(aes(x=time_from_start, y=value, color=group)) +
geom_point() +
facet_wrap(~name, scales="free") +
scale_color_manual(values=c("#19831C", "#A27FC9")) +
theme_classic() +
labs(title=paste("Compound Levels Across Time for", matrix),
x="Time From Start (min)",
y="Concentration (pg/mL)") +
theme(legend.position="bottom",
legend.title=element_blank(),
strip.background=element_blank(),
plot.title.position="plot")
}
plot_scatter_time(matrix = "WB")
<<<<<<< HEAD
<<<<<<< HEAD
plot_scatter_time(matrix = "OF")
plot_scatter_time(matrix = "BR")
## Warning: Removed 1526 rows containing missing values or values outside the scale range
## (`geom_point()`).
plot_scatter_time(matrix = "OF")
## Warning: Removed 1938 rows containing missing values or values outside the scale range
## (`geom_point()`).
plot_scatter_time(matrix = "BR")
## Warning: Removed 7592 rows containing missing values or values outside the scale range
## (`geom_point()`).
plot_scatter_time(matrix = "OF")
plot_scatter_time(matrix = "BR")
ggplot(combined, aes(x = time_from_start, y = thc)) +
geom_point(size = 1, color = "lightgreen") +
facet_wrap(~ fluid_type, scales = "free_y") +
labs(
title = "THC Levels across Time in each Fluid Type",
x = "Time from Start (minutes)",
y = "Concentration (pg/mL)",
) +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5),
legend.position = "top"
)
<<<<<<< HEAD
<<<<<<< HEAD
## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_point()`).
ggplot(combined, aes(x = timepoint, y = thc, color = treatment, group = treatment)) +
geom_line() +
geom_point(position = position_jitter(width = 0.2), size = 2) + # Add jitter for better visibility
facet_wrap(~ fluid_type, scales = "free_y") + # Create separate plots for each fluid type
labs(
title = "THC Levels Across Fluid Types Over Time",
x = "Time Interval",
y = "THC Level",
color = "Dosage"
) +
theme_minimal(base_size = 14) + # Increase base font size for readability
theme(
plot.title = element_text(hjust = 0.5),
axis.text.x = element_text(angle = 45, hjust = 1), # Rotate x-axis labels for readability
legend.position = "top"
)
<<<<<<< HEAD
ggplot(combined %>% filter(fluid_type == "WB" & time_from_start >= 0 & time_from_start <= 180), aes(x = time_from_start, y = thc, color = treatment, group = treatment)) +
geom_line() +
geom_point(position = position_jitter(width = 0.2), size = 2) + # Add jitter for better visibility
labs(
title = "THC Levels Across Water Blood Over Time",
x = "Time Interval",
y = "THC Level",
color = "Dosage"
) +
theme_minimal(base_size = 14) + # Increase base font size for better readability
theme(
plot.title = element_text(hjust = 0.5),
axis.text.x = element_text(angle = 45, hjust = 1), # Rotate x-axis labels
legend.position = "bottom"
)
<<<<<<< HEAD
ggplot(combined %>% filter(fluid_type == "WB" & time_from_start >= 0 & time_from_start <= 60), aes(x = time_from_start, y = thc, color = treatment, group = treatment)) +
geom_line() +
geom_point(position = position_jitter(width = 0.2), size = 2) + # Add jitter for better visibility
labs(
title = "THC Levels Across Water Blood Over Time",
x = "Time Interval",
y = "THC Level",
color = "Dosage"
) +
theme_minimal(base_size = 14) + # Increase base font size for better readability
theme(
plot.title = element_text(hjust = 0.5),
axis.text.x = element_text(angle = 45, hjust = 1), # Rotate x-axis labels
legend.position = "bottom"
)
<<<<<<< HEAD
ggplot(combined %>% filter(fluid_type == "WB" & time_from_start >= 0 & time_from_start <= 60), aes(x = time_from_start, y = thc, color = treatment, group = treatment)) +
geom_line() +
geom_point(position = position_jitter(width = 0.2), size = 2) +
facet_wrap(~treatment) +
labs(
title = "THC Levels Across Water Blood Over Time",
x = "Time Interval",
y = "THC Level",
color = "Dosage"
) +
theme_minimal(base_size = 14) + # Increase base font size for better readability
theme(
plot.title = element_text(hjust = 0.5),
axis.text.x = element_text(angle = 45, hjust = 1), # Rotate x-axis labels
legend.position = "bottom"
)
<<<<<<< HEAD
```
>>>>>>> origin/main =======Pearson, J., et al. (2020). Legal implications of THC per se limits. Journal of Accident Analysis & Prevention. Retrieved from Taylor & Francis Online.↩︎
Huestis, M. A. (2007). Human cannabinoid pharmacokinetics. Chemistry & Biodiversity, 4(8), 1770-1804. [https://pmc.ncbi.nlm.nih.gov/articles/PMC2689518/).↩︎
Compton, R. (2017). Marijuana-impaired driving: A report to Congress. National Highway Traffic Safety Administration. Retrieved from NGTSA.↩︎